package pl.edu.agh.jemo.evolution.specimen.impl;

import java.util.ArrayList;
import java.util.List;

import pl.edu.agh.jemo.evolution.specimen.Specimen;

/**
 * Extension of Specimen dedicated to use with NSGA2 algorithm.
 * It introduces some more parameters used by selection needed by nsga2 algorithm.
 * It doesn't provide any more functionallity than Specimen class.
 * 
 * @author Marcin Zbijowski
 *
 */
public class NSGA2Specimen extends Specimen {

	/**
	 * Number of specimen, that dominates this specimen.
	 */
	private int dominatorsCount;
	/**
	 * List of all specimen dominated by this specimen.
	 */
	private List<NSGA2Specimen> dominateSet;
	/**
	 * Distance between two nearest neighbours of specimen.
	 */
	private Double distance = 0.;

	/**
	 * Returns current distance between two nearest neighbours of specimen.
	 * Result might often be 0. as distance is calculated only if needed.
	 * 
	 * @return Value of distance parameter.
	 */
	public Double getDistance() {
		return distance;
	}

	/**
	 * Stores calculated distance between two neighbours of specimen.
	 * 
	 * @param distance New value of distance parameter.
	 */
	public void setDistance(Double distance) {
		this.distance = distance;
	}

	/**
	 * Stores list of specimen dominated by this specimen.
	 * 
	 * @param dominateSet List of specimen, to be saved in dominateSet parameter.
	 */
	public void setDominateSet(List<NSGA2Specimen> dominateSet) {
		this.dominateSet = dominateSet;
	}

	/**
	 * Returns list of all specimen dominated by this specimen.
	 * The list might be empty or contain incorrect values, as it is only set when needed, and not updated later.
	 * 
	 * @return List of NSGA2Specimen stored in dominate set parameter.
	 */
	public List<NSGA2Specimen> getDominateSet() {
		return dominateSet;
	}

	/**
	 * Stores new number of specimen, that dominate current specimen.
	 * 
	 * @param dominatorsCount New value of dominatorsCount parameter.
	 */
	public void setDominatorsCount(int dominatorsCount) {
		this.dominatorsCount = dominatorsCount;
	}

	/**
	 * Returns number of specimen dominating this specimen.
	 * This value is only set if needed, so You shouldn't depend on returned value, except if You are sure, You set them earlier.
	 * 
	 * @return Value of dominators count parameter.
	 */
	public int getDominatorsCount() {
		return dominatorsCount;
	}
	
	/**
	 * Sets all values of new specimen, to be identical with object.
	 * This method is used by clone method. It has been divided from clone to allow it's usage from subclasses and prevent code duplication.
	 * It copies everything copied by Specimen class, distance, dominate set (copies references to dominated specimen), and dominators count.
	 * 
	 * @param spec Specimen, for which values are set to copies of object.
	 */
	protected void prepareCopy(NSGA2Specimen spec) {
		super.prepareCopy(spec);
		spec.distance = distance;
		if (dominateSet != null) {
			spec.dominateSet = new ArrayList<NSGA2Specimen>();
			spec.dominateSet.addAll(dominateSet);
		} else {
			spec.dominateSet = null;
		}
		spec.dominatorsCount = dominatorsCount;
	}
	
	/**
	 * Creates new specimen identical to object, and of the same type as object.
	 * 
	 * @return New specimen, that is copy of current specimen.
	 */
	@Override
	public NSGA2Specimen clone() {
		NSGA2Specimen result = null;
		try {
			result = new NSGA2Specimen();
			prepareCopy(result);	
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	/**
	 * Returns string format human readable specimen summary.
	 * Return srting contains info about genotype, phenotype and fitness of specimen.
	 * It also contains NSGA2Specimen info - dominators count, number of dominated specimen and distance value.
	 * 
	 * @return String format human readable specimen summary
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append("NSGA2:\n");
		sb.append("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
		sb.append("DOMINATORS COUNT:\n");
		sb.append(dominatorsCount);
		sb.append("\n");
		sb.append("DOMINATED COUNT:\n");
		if (dominateSet != null) {
			sb.append(dominateSet.size());
		} else {
			sb.append(0);
		}
		sb.append("\n");
		sb.append("DISTANCE:\n");
		sb.append(String.format("%.4f", distance));
		sb.append("\n");
		sb.append("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
		return sb.toString();
	}

}
